package org.uc3m.pfc.ui;

import java.util.ArrayList;
import java.util.List;

import org.uc3m.pfc.data.Action;
import org.uc3m.pfc.lifecycle.LifeCycle;
import org.uc3m.pfc.lifecycle.State;
import org.uc3m.pfc.speech.ISpeechManager;
import org.uc3m.pfc.speech.SpeechManager;

import android.os.CountDownTimer;

/**
 * 
 * @author esteban
 *
 */
public final class ActionSelector extends LifeCycle implements IActionSelector {

	// Class fields *******************************************************
	
	/** Timeout for actions buttons. */
	private static final long TIME_OUT_FOR_ACTION_BUTTONS = 5000;

	/** Delay for toast. */
	private static final int SHORT_DELAY = 2500; // 2'5 seconds
	
	/** Static instance. */
	private static ActionSelector selector;
	/** Types of selectors. */
	private List<ActionSelectorType> types;

	/** Object for synchronization. */
	private Object syncToken = new Object();
	
	/** Speech manager instance. */
	private ISpeechManager speechManager;

	/** UI utility instance. */
	private GuiUtils guiUtils;

	/** Current action selected, null if there isn't. */
	private String action;

	
    // Constructor *******************************************************
	
	/**
	 * Private default constructor.
	 */
	private ActionSelector() {
	
		types = new ArrayList<ActionSelectorType>();

		// TODO default values from config file
		types.add(ActionSelectorType.TXT);
//		types.add(MultiModalType.TTS);
//		types.add(MultiModalType.RECOG);
		
		speechManager = SpeechManager.getInstance();
		guiUtils = GuiUtils.getInstance();
		
		setState(State.stopped);
	}
	
	/**
	 * Get the selector.
	 * 
	 * @return static instance
	 */
	public static ActionSelector getSelector() {
	
		if (selector == null) {
			selector = new ActionSelector();
		}
		
		return selector;
	}

	
    // IActionSelector methods **********************************************
	
	@Override
	public String selectAction(final String deviceName, final 
			List<Action> actions) {

		setState(State.selecting);
		
		action = null;
		
		// append actions
		String actionMessage = "";
		List<String> actionsName = new ArrayList<String>();
		for (Action actionAux : actions) {
			
			// tts message
			actionMessage += actionAux + " ";
			
			// words to recognize, always in lower case
			actionsName.add(actionAux.getName().toLowerCase());
		}
		
		// TODO string from resources
		String message = "Opciones: " + actionMessage;

		// user input TXT, show menu
		if (types.contains(ActionSelectorType.TXT)) {
			
			guiUtils.showActions(actions);
		}

		// user input TTS
		if (types.contains(ActionSelectorType.TTS)
				&& !types.contains(ActionSelectorType.RECOG)) {

			// block until speak is finished or user click on button
			speechManager.speak(message);		
		}

		// user input voice recognition
		if (types.contains(ActionSelectorType.RECOG)
				&& !types.contains(ActionSelectorType.TTS)) {
			
			// block until recognition is finished or user click on button
			String actionAux = speechManager.recognize(actionsName);
			
			// action value can be set from button in the UI, check null first
			if (action == null) {
				action = actionAux;
			}
		}

		// user input TTS and voice recognition
		if (types.contains(ActionSelectorType.TTS)
				&& types.contains(ActionSelectorType.RECOG)) {
			
			// block until recognition is finished or user click on button
			String actionAux = speechManager.speechAndRecognize(message, 
					actionsName);

			// action value can be set from button in the UI, check null first
			if (action == null) {
				action = actionAux;
			}
		}
		
		// in case we havent't choose an option, wait for user clicks on button 
		if (action == null && types.contains(ActionSelectorType.TXT)
				&& !types.contains(ActionSelectorType.RECOG)) {
			
			
			// wait for user to click a button
			waitForUserInputButton();
			
		}

		// user input TXT, hide menu
		if (types.contains(ActionSelectorType.TXT)) {

			guiUtils.hideActions();
		}

		setState(State.stopped);
		
		return action;
	
	}

	@Override
	public void notifyUserInputButton(final String paction) {
		
		this.action = paction;
		
        synchronized (syncToken) {
            syncToken.notify();
        }
        
        // cancel speech manager
        speechManager.stopAll();
	}

	@Override
	public void showMessage(final String message) {
		
		setState(State.showing);

		long millisStart = 0;

		// user input TXT
		if (types.contains(ActionSelectorType.TXT)) {

			millisStart = System.currentTimeMillis();
			
			guiUtils.showToast(message);
		}

		// user input TTS
		if (types.contains(ActionSelectorType.TTS)) {
		
			speechManager.speak(message);
		}

		// wait for toast ending
		long millisEnd = System.currentTimeMillis();
		waitForToast(millisStart, millisEnd);
		
		setState(State.stopped);
	}	
	
	@Override
	public void setTypes(final List<ActionSelectorType> ptypes) {
		this.types = ptypes;
	}
	
	
    // Private methods ****************************************************
	
	/**
	 * Wait until voice recognition is finished.
	 */
	private void waitForUserInputButton() {

		actionButtonsTimeOut.start();
		
        synchronized (syncToken) {
            try {
                syncToken.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }		
		
	}
	
	/** Count down timer for click on actions buttons. */
	private CountDownTimer actionButtonsTimeOut = 
			new CountDownTimer(TIME_OUT_FOR_ACTION_BUTTONS, 
					TIME_OUT_FOR_ACTION_BUTTONS) {

		@Override
		public void onTick(final long millisUntilFinished) {
		}

		@Override
		public void onFinish() {

			// timer finish, notify with action null
			if (action == null) {
				notifyUserInputButton(null);
			}

		}

	};
	


	/**
	 * Wait for toast pop-up ending.
	 * 
	 * @param millisStart start time, 0 if toast isn't launched. 
	 * @param millisEnd end time
	 */
	private void waitForToast(final long millisStart, final long millisEnd) {
		
		long delay = SHORT_DELAY;
		
		if (millisStart != 0) {
			
			// wait for toast ending
			
			long dif = millisEnd - millisStart;
			
			long timeToSleep = delay - dif;
			
			if (timeToSleep > 0) {
				
				try {
					Thread.sleep(timeToSleep);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}
		
	}

	
}
